package com.lf.api.weixin;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.lf.api.Constants;
import com.lf.api.weixin.entity.AccessToken;
import com.lf.api.weixin.entity.JSTicket;
import com.lf.api.weixin.entity.Menu;
import com.lf.api.weixin.entity.ResultCode;
import com.lf.api.weixin.entity.SNSUserInfo;
import com.lf.api.weixin.entity.WeiXinAccount;
import com.lf.api.weixin.entity.WeixinOauth2Token;
import com.lf.api.weixin.entity.WeixinUserInfo;

/**
 * 
 * Description: 微信工具类
 * Copyright: Copyright (c)2012
 * Company: 小小书童
 * @author: 小小书童
 * @version: 1.0
 * Create at: 2016年10月1日 上午12:09:07
 *
 * Modification History:
 * Date Author Version Description
 * ---------------------------修改历史---------------------------------------
 * 2016年10月1日 小小书童 1.0
 */
public final class WeiXinUtil {
    private static Logger log = LoggerFactory.getLogger(WeiXinUtil.class);
    
    /**
     * 发送https请求
     * 
     * @param requestUrl 请求地址
     * @param requestMethod 请求方式（GET、POST）
     * @param outputStr 提交的数据
     * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
     */
    public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
        try {
            // 创建SSLContext对象，并使用我们指定的信任管理器初始化
            TrustManager[] tm = { new MyX509TrustManager() };
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();

            URL url = new URL(requestUrl);
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setSSLSocketFactory(ssf);
            
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            // 设置请求方式（GET/POST）
            conn.setRequestMethod(requestMethod);

            // 当outputStr不为null时向输出流写数据
            if (null != outputStr) {
                OutputStream outputStream = conn.getOutputStream();
                // 注意编码格式
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }

            // 从输入流读取返回内容
            InputStream inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }

            // 释放资源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            conn.disconnect();
            return buffer.toString();
        } catch (ConnectException ce) {
            log.error("连接超时：{}", ce);
        } catch (Exception e) {
            log.error("https请求异常：{}", e);
        }
		return null;
    }

    /**
     * 获取接口访问凭证
     * 
     * @param appid 凭证
     * @param appsecret 密钥
     * @return
     */
    public static AccessToken getAccessToken(WeiXinAccount account) {
        String requestUrl = Constants.WeiXinURL.TOKEN_URL.replace("APPID", account.getAppID()).replace("APPSECRET", account.getAppsecret());
        return (AccessToken)convert2Object(requestUrl, AccessToken.class);
    }
    
    /**
     * 获取用户信息
     * 
     * @param accessToken 接口访问凭证
     * @param openId 用户标识
     * @return WeixinUserInfo
     */
    public static WeixinUserInfo getUserInfo(String accessToken, String openId) {
        String requestUrl = Constants.WeiXinURL.USER_INFO_URL.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
        return (WeixinUserInfo)convert2Object(requestUrl, WeixinUserInfo.class);
    }
    
    /**
     * 获取票据
     * 
     * @param accessToken 接口访问凭证
     * @return JSTicket
     */
    public static JSTicket getJSTicket(String accessToken) {
        String requestUrl = Constants.WeiXinURL.JSAPI_TICKET_URL.replace("ACCESS_TOKEN", accessToken);
        return (JSTicket)convert2Object(requestUrl, JSTicket.class);
    }
    
    /**
     * 获取网页授权凭证
     * 
     * @param appId 公众账号的唯一标识
     * @param appSecret 公众账号的密钥
     * @param code
     * @return WeixinAouth2Token
     */
    public static WeixinOauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {
        String requestUrl = Constants.WeiXinURL.OAUTH2_ACCESS_TOKEN_URL.replace("APPID", appId).replace("SECRET", appSecret).replace("CODE", code);
        return (WeixinOauth2Token)convert2Object(requestUrl, WeixinOauth2Token.class);
    }
    
    /**
     * 
     * Description:网页获取用户信息
     * Date:2016年10月6日
     * @author 小小书童
     * @return SNSUserInfo
     */
    public static SNSUserInfo getSNSUserInfo(String accessToken, String openId) {
    	String requestUrl = Constants.WeiXinURL.SNS_USERINFO_URL.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
    	return (SNSUserInfo)convert2Object(requestUrl, SNSUserInfo.class);
	}
    
    public static SNSUserInfo downloadMedia(String accessToken, String mediaId) {
    	String requestUrl = Constants.WeiXinURL.DOWNLOAD_URL.replace("ACCESS_TOKEN", accessToken).replace("MEDIA_ID", mediaId);
    	
    	return (SNSUserInfo)convert2Object(requestUrl, SNSUserInfo.class);
	}
    
    /**
     * 创建菜单
     * 
     * @param menu 菜单实例
     * @param accessToken 有效的access_token
     * @return 0表示成功，其他值表示失败
     */
    public static ResultCode createMenu(Menu menu, String accessToken) {
        String requestUrl = Constants.WeiXinURL.MENU_CREATE_URL.replace("ACCESS_TOKEN", accessToken);
        String jsonMenu = JacksonUtil.bean2JSon(menu);
        // 调用接口创建菜单
        log.info("创建菜单jsonMenu=" + jsonMenu);
        String result = httpsRequest(requestUrl, "POST", jsonMenu);
        if (StringUtils.isEmpty(result)){
        	return null;
        }
        ResultCode entity = JacksonUtil.json2Bean(result, ResultCode.class);
        if(entity.getErrorCode() != 0){
            log.error("创建菜单失败 errcode:{} errmsg:{}", entity.getErrorCode(), entity.getErrorMsg());
        }
        return entity;
    }
    
    /**
     * 
     * Description:对象组装
     * Date:2016年10月1日
     * @author 小小书童
     * @return T
     */
    private static ResultCode convert2Object(String requestUrl, Class<? extends ResultCode> valueType){
    	log.info("requestUrl=" + requestUrl);
    	String jsonStr = httpsRequest(requestUrl, "GET", null);
        if (StringUtils.isEmpty(jsonStr)) {
        	return null;
        }
        ResultCode t = null;
        try {
        	t = JacksonUtil.json2Bean(jsonStr, valueType);
        } catch (Exception e) {
        	log.error("获取token失败 errcode:{} errmsg:{}", t.getErrorCode(), t.getErrorMsg());
        }
        log.info("convert2Object=" + t);
        return t;
    }
    
    /**
     * URL编码（utf-8）
     * 
     * @param source
     * @return
     */
    public static String urlEncodeUTF8(String source) {
        String result = source;
        try {
            result = java.net.URLEncoder.encode(source, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return result;
    }
    
    /**
     * 根据内容类型判断文件扩展名
     * 
     * @param contentType 内容类型
     * @return
     */
    public static String getFileExt(String contentType) {
        String fileExt = "";
        if ("image/jpeg".equals(contentType))
            fileExt = ".jpg";
        else if ("audio/mpeg".equals(contentType))
            fileExt = ".mp3";
        else if ("audio/amr".equals(contentType))
            fileExt = ".amr";
        else if ("video/mp4".equals(contentType))
            fileExt = ".mp4";
        else if ("video/mpeg4".equals(contentType))
            fileExt = ".mp4";
        return fileExt;
    }
    
 // 将字节数组转换为十六进制字符串
 	private static String byteArrayToHexString(byte[] bytearray) {
 		String strDigest = "";
 		for (int i = 0; i < bytearray.length; i++) {
 			strDigest += byteToHexString(bytearray[i]);
 		}
 		return strDigest;
 	}

 	// 将字节转换为十六进制字符串
 	private static String byteToHexString(byte ib) {
 		char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
 		char[] ob = new char[2];
 		ob[0] = Digit[(ib >>> 4) & 0X0F];
 		ob[1] = Digit[ib & 0X0F];
 		String s = new String(ob);
 		return s;
 	}
     
 	public static String countSignature(String jsapi_ticket, String noncestr, String timestamp, String requestUrl){
 		String sign = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + noncestr+ "&timestamp=" + timestamp + "&url=" + requestUrl;
 		return SHADigest(sign);
 	}
 	
 	public static boolean checkAccessSignature(String signature,String token, 
 			String timestamp, String nonce){
 	    // 1.将token、timestamp、nonce三个参数进行字典序排序
        String[] arr = new String[] { token, timestamp, nonce };
        Arrays.sort(arr);
        
        // 2. 将三个参数字符串拼接成一个字符串进行sha1加密
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }
        String tmpStr = SHADigest(content.toString());
        
        // 3.将sha1加密后的字符串可与signature对比，标识该请求来源于微信
        log.info("count stmpStr=" + tmpStr + ", signature=" + signature);
        return tmpStr != null ? tmpStr.equals(signature.toLowerCase()) : false;
 	}

 	//SHA加密
	private static String SHADigest(String content) {
		log.info("content=" + content);
        MessageDigest md = null;
        String tmpStr = null;
        try {
            md = MessageDigest.getInstance("SHA-1");
            // 将三个参数字符串拼接成一个字符串进行sha1加密
            byte[] digest = md.digest(content.getBytes());
            tmpStr = byteArrayToHexString(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
		return tmpStr.toLowerCase();
	}
 	
 	public static void main(String[] args) {
 		/*String accessToken = getAccessToken(WeiXinAccount.TEST).getAccessToken();
		JSTicket ticket = getJSTicket(accessToken);
		System.out.println(ticket.getTicket());*/
		
		WeixinUserInfo user = getUserInfo("mXAynWooSLcRJiazscMihQo7-4zbm_HK4I_o26tEEB5CxzSIkfSitEhkeEB_54WND103acGRcfNbT9NWe9T0fHEJy80n8Lr_Jq-tRHsqU54", "oRP5gsyLtHKQoke2zKWOsTUDlnDI");
		System.out.println(user);
 	}

}